#include <iostream>
#include <QtOpenGL>

#include "glwidget.h"
#include "input.h"
#include "output.h"
#include "face.h"

GLWidget::GLWidget(QWidget* _parent) : QGLWidget(_parent){
	xMov = 0;
	yMov = 0;
	zMov = 0;
	xRot = 0;
	yRot = 0;
	zRot = 0;
	zoom = 2.0;
    scene = new Scene();

	light0_pos[0] = 0.0;
	light0_pos[1] = -10.0;
	light0_pos[2] = 20.0;
	light0_pos[3] = 1.0;

    light0_color[0] = 0.8;
    light0_color[1] = 0.8;
    light0_color[2] = 0.8;
    light0_color[3] = 1.0;

    showAxis = true;
    showGrid = true;
    showVertices = true;
    showEdges = false;
    showWireframe = false;
    showOpaque = false;
    showNormal = false;
//    showStepByStep = false;
}

GLWidget::~GLWidget() {
}

QSize GLWidget::minimumSizeHint() const {
	return QSize(50, 50);
}

QSize GLWidget::sizeHint() const {
	return QSize(400, 400);
}

Scene *GLWidget::newScene(){
    if(scene) delete scene;
    scene = new Scene();
	updateGL();
	return scene;
}

Scene *GLWidget::getScene(){
    return scene;
}

std::vector<Object *> GLWidget::loadScene(QFile *_file){
    Input input(_file);

    if(scene) delete scene;
	scene = input.parseScene();

	updateGL();
	return scene->getObjects();
}

Object *GLWidget::loadObject(QFile *_file){
    Input input(_file);

    if(!scene) scene = new Scene();
    Object *obj = input.parseObject(scene);
	updateGL();
	return obj;
}

void GLWidget::saveScene(QFile *_file){
    Output output(_file);

    if(!scene){
        std::cout << "Não existe cena para ser salva" <<std::endl;
        return;
    }
	output.parseScene(scene);
}

void GLWidget::saveObject(QFile *_file){
    Output output(_file);

    //por enquanto está salvando o primeiro
    //***colocar para salvar o selecionado
    Object *obj = scene->getObject(0);
    if(!obj){
        std::cout << "Objeto não existe" <<std::endl;
        return;
    }

	output.parseObject(obj);
}

std::vector<Object *> GLWidget::loadIn(QFile *_file){
    Input input(_file);

    if(scene) delete scene;
	scene = input.parseIn();

	updateGL();
	return scene->getObjects();
}

std::vector<Object *> GLWidget::loadOut(QFile *_file){
    Input input(_file);

    if(scene) delete scene;
	scene = input.parseOut();

	updateGL();
	return scene->getObjects();
}

void GLWidget::saveIn(QFile *_file){
    Output output(_file);

    if(!scene){
        std::cout << "Não existe cena para ser salva" <<std::endl;
        return;
    }
	output.parseIn(scene);
}

void GLWidget::saveOut(QFile *_file){
    Output output(_file);

    if(!scene){
        std::cout << "Não existe cena para ser salva" <<std::endl;
        return;
    }
	output.parseOut(scene);
}

void GLWidget::saveWed(QFile *_file){
    Output output(_file);

    if(!scene){
        std::cout << "Não existe cena para ser salva" <<std::endl;
        return;
    }
	output.parseWed(scene);
}

void GLWidget::convexHull(){
    if(scene) scene->convexHull();
	updateGL();
}

void GLWidget::tetrahedralization(){
    if(scene) scene->tetrahedralization();
	updateGL();
}

void GLWidget::wheelEvent(QWheelEvent *_event){
//    std::cout << "wheel" <<std::endl;
    if(_event->delta() > 0){
        if (zoom > 1){
            if(_event->modifiers() & Qt::ShiftModifier)
                zoom -= 2.0;
            else
                zoom -= 0.1;
        }else if(zoom > 0.1){
            if(_event->modifiers() & Qt::ShiftModifier)
                zoom -= 0.5;
            else
                zoom -= 0.01;
        }
    }
    else{
        if (zoom < 100.0){
            if(_event->modifiers() & Qt::ShiftModifier)
                zoom += 2.0;
            else
                zoom += 0.1;
        }
    }
    resizeGL(width(), height());
    updateGL();
}

void GLWidget::mouseMoveEvent(QMouseEvent *_event){
//    std::cout << "mouse" <<std::endl;
	int dx = _event->x() - lastPos.x();
	int dy = _event->y() - lastPos.y();

	if (_event->buttons() & Qt::LeftButton) {
        if (_event->modifiers() & Qt::ShiftModifier) {
            if ( fabs(lastPos.x() - _event->x()) > 3 ){
                int dx = -1;
                if(lastPos.x() - _event->x() < 0.0) dx = 1;

                xMov += 0.5 * dx;
                updateGL();
            }
            if ( fabs(lastPos.y() - _event->y()) > 3 ){
                int dy = 1;
                if(lastPos.y() - _event->y() < 0.0) dy = -1;

                yMov += 0.5*dy;
                updateGL();
            }

		} else {
            setXRotation(xRot + 8 * dy);
            setYRotation(yRot + 8 * dx);
		}
	} else if (_event->buttons() & Qt::RightButton) {
        if (_event->modifiers() & Qt::ShiftModifier) {
            if ( fabs(lastPos.x() - _event->x()) > 3 ){
                int dx = -1;
                if(lastPos.x() - _event->x() < 0.0) dx = 1;

                xMov += 0.5 * dx;
                updateGL();
            }
            if ( fabs(lastPos.y() - _event->y()) > 3 ){
                int dz = 1;
                if(lastPos.y() - _event->y() < 0.0) dz = -1;

                zMov += 0.5*dz;
                updateGL();
            }

		} else {
            setXRotation(xRot + 8 * dy);
            setZRotation(zRot + 8 * dx);
		}
	}

    lastPos = _event->pos();
}

void GLWidget::mousePressEvent(QMouseEvent *_event){
	lastPos = _event->pos();
}

void GLWidget::setXRotation(int _angle){
	normalizeAngle(&_angle);
	if (_angle != xRot) {
		xRot = _angle;
		updateGL();
	}
}

void GLWidget::setYRotation(int _angle){
	normalizeAngle(&_angle);
	if (_angle != yRot) {
		yRot = _angle;
		updateGL();
	}
}

void GLWidget::setZRotation(int _angle){
	normalizeAngle(&_angle);
	if (_angle != zRot) {
		zRot = _angle;
		updateGL();
	}
}

void GLWidget::changeShowAxis(bool _b){
	showAxis = _b;
	updateGL();
}

bool GLWidget::isShowingAxis(){
	return showAxis;
}

void GLWidget::changeShowGrid(bool _b){
	showGrid = _b;
	updateGL();
}

bool GLWidget::isShowingGrid(){
	return showGrid;
}

void GLWidget::changeShowVertices(bool _b){
	showVertices = _b;
	updateGL();
}

bool GLWidget::isShowingVertices(){
	return showVertices;
}

void GLWidget::changeShowEdges(bool _b){
	showEdges = _b;
	updateGL();
}

bool GLWidget::isShowingEdges(){
	return showEdges;
}

void GLWidget::changeShowWireframe(bool _b){
	showWireframe = _b;
	updateGL();
}

bool GLWidget::isShowingWireframe(){
	return showWireframe;
}

void GLWidget::changeShowOpaque(bool _b){
	showOpaque = _b;
	updateGL();
}

bool GLWidget::isShowingOpaque(){
	return showOpaque;
}

void GLWidget::changeShowNormal(bool _b){
	showNormal = _b;
	updateGL();
}

bool GLWidget::isShowingNormal(){
	return showNormal;
}
//
//void GLWidget::changeShowStepByStep(bool _b){
//	showStepByStep = _b;
//}
//
//bool GLWidget::isShowingStepByStep(){
//	return showStepByStep;
//}

void GLWidget::initializeGL() {
	glClearColor(0.0, 0.0, 0.0, 1.0);
	glEnable(GL_DEPTH_TEST);
	glShadeModel(GL_FLAT);
//	glEnable(GL_CULL_FACE);

    glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
	glEnable(GL_COLOR_MATERIAL);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    //luz
    glLightfv(GL_LIGHT0, GL_POSITION, light0_pos);
    glLightfv(GL_LIGHT0, GL_DIFFUSE,  light0_color);

}

void GLWidget::paintGL(void) {
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();



    glPushMatrix();

	glTranslated(xMov, yMov, zMov);

    //rotacao
	glRotated(xRot / 16.0, 1.0, 0.0, 0.0);
	glRotated(yRot / 16.0, 0.0, 1.0, 0.0);
	glRotated(zRot / 16.0, 0.0, 0.0, 1.0);


	glColor3f(1.0, 1.0, 0.0);
//	//problema que dah no menor ou igual
////Point a(  1 ,  -1 ,  -1  );
////Point b(  1 ,  -1 ,  1  );
////Point c(  -1 ,  -1 ,  1  );
////
////Point d(  1 ,  1 ,  1  );
////Point e(  -1 ,  -1 ,  -1  );
////Point f(  -1 ,  1 ,  1  );
////
////
////Point a(  2 ,  1 ,  0  );
////Point b(  0 ,  0 ,  1  );
////Point c(  -2 ,  1 ,  0  );
////Point d(  0 ,  -1 ,  0  );
////Point e(  0 ,  0 ,  1  );
////Point f(  0 ,  0 ,  3  );
//
////
////    Point a( -1 ,  1 ,  -1);
////    Point b( -1 ,  1 ,  1);
////    Point c(1 ,  1 ,  1);
////    Point d(1 ,  1 ,  1);
////    Point e(1 ,  1 ,  -1);
////    Point f(-1 ,  1 ,  1 );
////
//
////
////
////    Point a( 1 , 1 , 1);
////    Point b( 1 ,-1 ,-1);
////    Point c(-1 ,-1 , 1);
////
////    Point d(1 ,  1 ,  1);
////    Point e(1 ,  -1 ,  -1);
////    Point f(1 ,  1 ,  -1 );
////
//
////
////    Point a(1 ,  1 ,  1);
////    Point b(1 ,  -1 ,  -1);
////    Point c(1 ,  1 ,  -1 );
////    Point d( 1 , 1 , 1);
////    Point e( 1 , 1 ,-1);
////    Point f( 1 ,-1 , 1);
////
////
////    Point a(1 , -1 ,  1);
////    Point b(0 ,  0 ,  0);
////    Point c(1 ,  1 ,  1 );
////    Point d( -1 , 1 , -1);
////    Point e( 0 , 0 ,0);
////    Point f( -1 ,1 , 1);
//
//
////
////    Point a(0 , 0 ,  0);
////    Point b(-1 ,  0 ,  1);
////    Point c(2 ,  0 ,  1 );
////    Point d( 0 , 1 , 0.5);
////    Point e( 0 , 0 ,0);
////    Point f( 0 ,-2 , 0.5);
//
//    Point a(0 , 0 ,  0);
//    Point b(-1 ,  0 ,  1);
//    Point c(2 ,  0 ,  1 );
//
//    Point d( 0 , 1 , 0.5);
//    Point e( 0 , 0 ,0);
//    Point f( 0 ,-2 , 0.5);
//
//    Point n1 = (b-a).crossProduct(c-a).normalization();
//    Point baricentro1 = ((a + b + c)*(1.0/3.0));
////
//    Point n2 = (e-d).crossProduct(f-d).normalization();
//    Point baricentro2 = ((d + e + f)*(1.0/3.0));
//
//    baricentro1 = Point();
//    baricentro2 = Point();
//
//    if( haveIntersection(a, b, c, d, e, f) ) std::cout <<"Tem interseção"<<std::endl;
//    else std::cout <<"NÂO tem interseção"<<std::endl;
//
//    glBegin(GL_POLYGON);
//        glColor3f(1.0, 1.0, 0.0);
//
//        glVertex3dv(a.intoVector());
//        glVertex3dv(b.intoVector());
//        glVertex3dv(c.intoVector());
//    glEnd();
//
//    glBegin(GL_LINE);
//        glLineWidth(3);
//        glColor3f(1.0, 0.,0.);
//        glVertex3dv(baricentro1.intoVector());
//        glVertex3dv((baricentro1 + n1).intoVector());
//
//        glLineWidth(1);
//    glEnd();
//
//    glBegin(GL_POLYGON);
//        glColor3f(1.0, 0.0, 1.0);
//
//        glVertex3dv(d.intoVector());
//        glVertex3dv(e.intoVector());
//        glVertex3dv(f.intoVector());
//    glEnd();
//
//    glBegin(GL_LINE);
//        glLineWidth(3);
//        glColor3f(1.0, 0.,0.);
//        glVertex3dv(baricentro2.intoVector());
//        glVertex3dv((baricentro2 + n2).intoVector());
//
//        glLineWidth(1);
//    glEnd();
//

    if (scene) scene->draw(showVertices, showEdges, showWireframe, showOpaque, showNormal/*, showStepByStep*/);


    if(showAxis){
        glDisable(GL_LIGHT0);
        glDisable(GL_LIGHTING);
        //eixos
        glLineWidth(2);

        glBegin(GL_LINES);
            glColor3f(1.0, 0.0, 0.0);
            //eixo X
            glVertex3f( 0, 0, 0);
            glVertex3f( 1, 0, 0);

            glColor3f(0.0, 1.0, 0.0);
            //eixo y
            glVertex3f( 0, 0, 0);
            glVertex3f( 0, 1, 0);

            glColor3f(0.0, 0.0, 1.0);
            //eixo z
            glVertex3f( 0, 0, 0);
            glVertex3f( 0, 0, 1);
        glEnd();
        glLineWidth(1);

        glEnable(GL_LIGHT0);
        glEnable(GL_LIGHTING);
    }
    if(showGrid){
        glDisable(GL_LIGHT0);
        glDisable(GL_LIGHTING);
        //eixos

        int square = 20;
        glColor4f(0.5, 0.5, 0.5, 0.5);
        glBegin(GL_LINES);
        for(int i = -square; i <= square; i++){
            glVertex3f( i, square, 0);
            glVertex3f( i, -square, 0);

            glVertex3f( square, i, 0);
            glVertex3f( -square, i, 0);
        }
        glEnd();

        glEnable(GL_LIGHT0);
        glEnable(GL_LIGHTING);

    }

    glPopMatrix();
}

void GLWidget::resizeGL(int _width, int _height) {
	glViewport(0, 0, _width, _height);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	double f = 0.30*zoom;
	if (_width<=_height)
		glFrustum(-f,     f,    -f*((double)_height)/_width,    f*((double)_height)/_width,     1.0, 50.0);
	else
		glFrustum(-f*((double)_width)/_height,  f*((double)_width)/_height,     -f,     f,    1.0, 50.0);

	gluLookAt(0.0, -10, 10.0,   0.0, 0.0, 0.0,  0.0, 0.0, 1.0);
	glMatrixMode(GL_MODELVIEW);
}

void GLWidget::normalizeAngle(int *_angle){
	while (*_angle < 0)
		*_angle += 360 * 16;
	while (*_angle > 360 * 16)
		*_angle -= 360 * 16;
}
